#include <cstdio>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <signal.h>
#include "locker.h"
#include "ThreadPool.h"
#include "http_conn.h"

#define MAX_FD 65535
#define MAX_EVENT 10000

void addsig(int sig, void(handler)(int))
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));

    sa.sa_handler = handler;
    sigfillset(&sa.sa_mask);
    sigaction(sig, &sa, NULL);
}

extern void addfd(int epollfd, int fd, bool one_shot);
extern void removefd(int epollfd, int fd);
extern void modfd(int epollfd, int fd, int ev);
extern void setreuse(int fd);

int main(int argc, char *argv[])
{
    if (argc <= 1)
    {
        printf("按照如下格式运行: %s prooooot\n", basename(argv[0]));
        exit(-1);
    }

    // 获取端口号
    int port = atoi(argv[1]);

    // 对SIGPIPE信号进行处理
    // 对一个已经对端关闭的文件描述符写入数据，
    // 会发送一个SIGPIPE信号，这个信号的默认动作是终止程序的运行
    addsig(SIGPIPE, SIG_IGN);

    // 创建线程池
    ThreadPool<http_conn> *pool = NULL;
    try
    {
        pool = new ThreadPool<http_conn>();
    }
    catch (...)
    {
        exit(-1);
    }

    // 创建一个数据用于保存所有的客户端信息
    http_conn *users = new http_conn[MAX_FD];

    // 网络逻辑
    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    // 设置端口复用
    //  int reuse = 1;
    //  setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
    setreuse(listenfd);
    // 绑定地址
    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(port);
    bind(listenfd, (struct sockaddr *)&address, sizeof(address));
    // 监听
    listen(listenfd, 5);
    // 创建epoll对象
    epoll_event events[MAX_EVENT];
    int epollfd = epoll_create(5);
    addfd(epollfd, listenfd, false);
    http_conn::m_epollfd = epollfd;
    http_conn::m_user_count = 0;

    while (true)
    {
        int num = epoll_wait(epollfd, events, MAX_EVENT, -1);
        if (num < 0 && errno != EINTR)
        {
            printf("epoll failure\n");
            break;
        }
        for (int i = 0; i < num; i++)
        {
            int sockfd = events[i].data.fd;
            if (sockfd == listenfd)
            {
                // 有客户端连接
                struct sockaddr_in client_address;
                socklen_t client_addrlen = sizeof(client_address);
                int connfd = accept(listenfd, (struct sockaddr *)&client_address, &client_addrlen);
                if (http_conn::m_user_count >= MAX_FD)
                {
                    // 连接数到达上限
                    // TODO 返回错误信息
                    close(connfd);
                    continue;
                }
                users[connfd].init(connfd, client_address);
            }
            else if (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) // 对端异常
            {
                users[sockfd].close_conn();
            }
            else if (events[i].events & EPOLLIN)
            {
                if (users[sockfd].read()) // 一次性读出所有数据
                {
                    
                    //往任务队列添加任务
                    pool->append(users + sockfd);
                }
                else
                {
                    users[sockfd].close_conn();
                }
            }
            else if (events[i].events & EPOLLOUT)
            {
                if (!users[sockfd].write()) //一次性写入所有数据
                {
                    users[sockfd].close_conn();
                }
            }
        }
    }
    close(epollfd);
    close(listenfd);
    delete[] users;
    delete pool;

    return 0;
}